///////////////////////////////////////////////////////////
//
//
// GLWorld.cpp	- Implementation of the OpenGL World component
//
//
//
#include <windows.h>
#include <math.h>

// STL Headers
#include <new.h>
#include <algorithm>
#include <xmemory>
#include <list>

#include <comcat.h>	// Component Categories

#include "util.h"

#include "CGL.h"
#include "GLWorld.h"

// MIDL generated header files
#include "Canvas_I.h"
#include "Model_I.h" 
#include "Visual_I.h"
#include "GLWrld_C.h" // For CLSID Definitions. 
#include "Canvas_C.h"

// Definition and Declaration of CATID_TangramWorldCategory 
#include <initguid.h>
#include "WorldCat.h" 

///////////////////////////////////////////////////////////
//
//	Global Constants
//
const GLdouble g_Rotation = -60.0 ; //-75.0 ; 
const GLdouble g_TranslateCameraBack = -1.0 ;
static const double PI = 3.141592654; 


///////////////////////////////////////////////////////////
//
// Creation function used by CFactory.
//
HRESULT CGLWorld::CreateInstance(IUnknown* pOuterUnknown,
									CUnknown** ppNewComponent ) 
{
   if (pOuterUnknown != NULL)
   {
      // Don't allow aggregation. Just for the heck of it.
      return CLASS_E_NOAGGREGATION;
   }
	
   *ppNewComponent = new CGLWorld(pOuterUnknown) ;
   return S_OK ;
}

///////////////////////////////////////////////////////////
//
//	SpecialReg -	Used to register and unregister the component
//						categories.
//
void CGLWorld::SpecialReg(BOOL bRegister)
{
	// Create the standard COM Category Manager
	ICatRegister* pICatRegister = NULL ;
	HRESULT hr = ::CoCreateInstance(	CLSID_StdComponentCategoriesMgr,
										NULL, CLSCTX_ALL, IID_ICatRegister,
										(void**)&pICatRegister) ;
	if (FAILED(hr))
	{
		ErrorMessage("Could not create the ComCat component.", hr);
		return ;
	}

	// Fill in a Category Info structure.
	CATEGORYINFO CatInfo ;
	CatInfo.catid				= CATID_TangramWorldCategory ;
	CatInfo.lcid				= LOCALE_SYSTEM_DEFAULT ;
	wcscpy(CatInfo.szDescription, L"Tangram World Component Category" ) ;

	// Array of Categories
	int cIDs = 1 ;
	CATID IDs[1] ;
	IDs[0] = CATID_TangramWorldCategory ;

	// Register or Unregister
	if (bRegister)
	{
		// Register the component category.
		hr = pICatRegister->RegisterCategories(1, &CatInfo) ;
		ASSERT_HRESULT(hr) ; 

		// Register this component as a member of TangramWorldCategory
		hr = pICatRegister->RegisterClassImplCategories(	CLSID_TangramGLWorld,
															cIDs, IDs ) ;
		ASSERT_HRESULT(hr) ; 
	}
	else
	{
		// Unregister the component from its categories.
		hr = pICatRegister->UnRegisterClassImplCategories(	CLSID_TangramGLWorld,
															cIDs, IDs);
		ASSERT_HRESULT(hr) ; 

		// --- Check to see if there are other components in this category before we delete it. ---

		// Get the ICatInformation interface.
		ICatInformation* pICatInformation = NULL ;
		hr = pICatRegister->QueryInterface(IID_ICatInformation, (void**)&pICatInformation) ;
		ASSERT_HRESULT(hr) ;

		// Get the IEnumCLSID interface.
		IEnumCLSID* pIEnumCLSID = NULL ;
		hr = pICatInformation->EnumClassesOfCategories(cIDs, IDs, 0, NULL, &pIEnumCLSID) ;
		ASSERT_HRESULT(hr) ;
	
		// Get the next CLSID in the list.
		CLSID clsid ;
		hr = pIEnumCLSID->Next(1, &clsid, NULL) ;
		ASSERT_HRESULT(hr) ;

		// If hr is S_FALSE, there no components are in this category, so remove it.
		if (hr == S_FALSE) 
		{
			// Unregister the component category
			hr = pICatRegister->UnRegisterCategories(cIDs, IDs) ;
			ASSERT_HRESULT(hr) ; 
		}

		// CleanUp
		pIEnumCLSID->Release() ;
		pICatInformation->Release() ;
	}

	if (pICatRegister)
	{
		pICatRegister->Release() ;
	}
}

///////////////////////////////////////////////////////////
//
//	NondelegatingQueryInterface
//
HRESULT __stdcall 
CGLWorld::NondelegatingQueryInterface(const IID& iid, void** ppv)
{ 	
	if (iid == IID_ITangramWorld)
	{
		return FinishQI(static_cast<ITangramWorld*>(this), ppv) ;
	}
	else if (iid == IID_ITangramGLWorld) 
	{
		return FinishQI(static_cast<ITangramGLWorld*>(this), ppv) ;
	}
	else if (iid == IID_ITangramCanvas)
	{
		return FinishQI(m_pCanvas, ppv) ;
	}
	else
	{
		return CUnknown::NondelegatingQueryInterface(iid, ppv) ;
	}
}

///////////////////////////////////////////////////////////
//
//	Constructor
//
CGLWorld::CGLWorld(IUnknown* pOuterUnknown)
: CUnknown(pOuterUnknown),
	m_pCanvas(NULL),
	m_pCanvasUnknown(NULL),
	m_pGL(NULL),
	m_hdc(NULL),
	m_hMainWnd(NULL),
	m_bPicking(FALSE)

{
	m_ptLowerLeft.x = m_ptLowerLeft.y = 0.0 ;
	m_ptUpperRight.x = m_ptUpperRight.y = 0.0;
}


///////////////////////////////////////////////////////////
//
//	Destructor
//
CGLWorld::~CGLWorld()
{
	CVisualList::iterator p ;
	for(p = m_VisualList.begin() ; p != m_VisualList.end() ; p++)
	{
		(*p)->Release() ;
	}

	// We already cleaned up the aggregated objects in FinalRelease.
	ASSERT(m_pCanvasUnknown == NULL) ;
	ASSERT(m_pCanvas == NULL) ;

	if (m_pGL != NULL) 
	{
		delete m_pGL ; 
	}
}

///////////////////////////////////////////////////////////
//
//	FinalRelease -- Clean up the aggreated objects.
//
void CGLWorld::FinalRelease()
{
	CUnknown::FinalRelease() ;

	// Release the ICanvas interface on the aggregated object.
	if (m_pCanvas != NULL)
	{
		// Add a reference back to the outer object.
		GetOuterUnknown()->AddRef() ;
		// Remove the reference on the inner and outer objects.
		m_pCanvas->Release() ;
		m_pCanvas = NULL ;
	}

	// Release our hold on the inner object.
	if (m_pCanvasUnknown != NULL)
	{
		m_pCanvasUnknown->Release() ;
		m_pCanvasUnknown = NULL ;
	}
}


///////////////////////////////////////////////////////////
//
//					ITangramWorld Implementation
//
///////////////////////////////////////////////////////////
//
//	Initialize
//
HRESULT CGLWorld::Initialize(HWND hWnd, double modelWidth, double modelHeight)
{
	// Preconditions
	if (m_pCanvas != NULL) 
	{
		ASSERT(m_pCanvas == NULL) ;
		return E_FAIL ;
	}

	if ((hWnd == NULL) || (!::IsWindow(hWnd)) || 
		(modelWidth == 0.0) || (modelHeight == 0.0))
	{
		ASSERT(hWnd != NULL) ;
		ASSERT(::IsWindow(hWnd)) ;
		ASSERT( m_pCanvas != NULL) ;
		ASSERT( (modelWidth != 0.0) && (modelHeight != 0.0)) ;
		return E_INVALIDARG ; 
	}

	// Cache the window handle.
	m_hMainWnd = hWnd ;

	// ----- Aggreate the canvas object to handle Canvas messages ------

	// Create the canvas on which the world draws.
	HRESULT hr = ::CoCreateInstance(	CLSID_TangramCanvas, 
										GetOuterUnknown(), 
										CLSCTX_INPROC_SERVER,
										IID_IUnknown, (void**)&m_pCanvasUnknown) ;
	if (FAILED(hr))
	{
		ErrorMessage("Could not aggregate the canvas component.", hr) ;
		return E_FAIL ;
	}

	//------ Get the ITangramCanvas interface -----

	// Query for the ITangramCanvas interface.
	hr = m_pCanvasUnknown->QueryInterface(IID_ITangramCanvas, (void**)&m_pCanvas) ;
	if (FAILED(hr))
	{
		ErrorMessage("Could not get required interface from Canvas.", hr) ;
		m_pCanvas->Release() ;
		m_pCanvas = NULL ;
		return E_FAIL ;
	}

	// Release the AddRef placed on the outer object by the QI call above.
	GetOuterUnknown()->Release() ;

	// ----- Initialize the canvas component. -----
	RECT rectClient ;
	::GetClientRect(hWnd, &rectClient) ;

	int widthClient = rectClient.right - rectClient.left ;
	int heightClient = rectClient.bottom - rectClient.top ;
	hr = m_pCanvas->Initialize(hWnd, widthClient, heightClient) ;
	ASSERT_HRESULT(hr) ; 

	// ------ Create the OpenGL HGRC ------

	// Get the dc from the canvas.
	HDC hdc = NULL ;
	hr = m_pCanvas->GetHDC(&hdc) ;
	ASSERT_HRESULT(hr) ;

	ASSERT(hdc != NULL) ; 
	m_hdc = hdc ;

	// Create the OpenGL Context object.
	m_pGL = new CGL ;
	m_pGL->Create(m_hdc) ;

	// Set up the palette.
	HPALETTE hPalTemp = m_pGL->GetPalette() ;	
	if (hPalTemp)
	{
		m_pCanvas->SetPalette(hPalTemp) ; 
	}

	// Set up CGL.
	m_pGL->MakeCurrent(); // MakeCurrent After the palette is set!
	GLResize(widthClient, heightClient) ;
	GLSetup() ;
	
	return S_OK;
}

///////////////////////////////////////////////////////////
//
// DeviceToModel -- Do not use with size parameters because addin the org...
//
#pragma optimize( "p", on ) // The following function doesn't work without this.

HRESULT CGLWorld::DeviceToModel(POINT ptIn, TangramPoint2d* pptdOut)
{
	if (!IsValidAddress(pptdOut, sizeof(TangramPoint2d), TRUE))
	{
		ASSERT(0) ;
		return E_POINTER ;
	}
	GLdouble matrixModel[16] ; 
	GLdouble matrixProj[16];
	GLint viewport[4] ;
	glGetIntegerv(GL_VIEWPORT, viewport) ; 
	glGetDoublev( GL_MODELVIEW_MATRIX, matrixModel) ;
	glGetDoublev(GL_PROJECTION_MATRIX, matrixProj) ;

	// Windows GDI device units have 0,0 as upper left corner.
	// OpenGL puts 0,0 in lower left hand corner. Change it.
	double Xw = static_cast<double>(ptIn.x) ;
	double Yw = static_cast<double>(viewport[3] - ptIn.y) ;

	double Tx = m_ptLowerLeft.x ; 
	double Ty = m_ptLowerLeft.y ;
	double Tz = g_TranslateCameraBack ;
	double r = m_ptUpperRight.x ;
	double t = m_ptUpperRight.y ;
	const double n = 1.0 ;
	const double f = 50.0 ;

	double Xn = (2.0 * Xw / static_cast<double>(viewport[2])) - 1.0 ;
	double Yn = (2.0 * Yw / static_cast<double>(viewport[3])) - 1.0 ;

	double sinRot = sin(g_Rotation*PI/180.0) ;
	double cosRot = cos(g_Rotation*PI/180.0) ;

	//double y = -((2*f*n/(f+n)) + Tz)/sinRot ;
	double y = -((Yn*t*Tz/n) + Ty)/(cosRot + (Yn*t*sinRot/n)) ;
	double x = -Xn*r*(y*sinRot + Tz)/n - Tx ;

	pptdOut->x = (x * 20.0 / (m_ptUpperRight.x * 2.0) );
	pptdOut->y = (y * 20.0 / (m_ptUpperRight.y * 2.0) );

	return S_OK ;
}

#pragma optimize( "p", off ) 

///////////////////////////////////////////////////////////
//
// VisualFromPoint -	Finds the visual which contains POINT pt.
//						Returns the requested IID for the visual.
//	
//	Results:
//			S_OK	 -	Found a Visual.
//			S_FALSE -	No visual containing this point. 
//						*ppITangramVisual will be NULL.
//
HRESULT CGLWorld::VisualFromPoint(	POINT pt,
									REFIID iid, 
									IUnknown** ppITangramVisual) 
{
	// Preconditions.
	if (!IsValidInterfaceOutParam(ppITangramVisual))
	{
		ASSERT(0) ;
		return E_POINTER ;
	}

	// Initialize out parameter.
	*ppITangramVisual = NULL ;

	// Find the shape we clicked on.

	int index = Pick(pt) ;
	// Search the list in reverse order because the
	// most recently selected and last painted object is
	// on the end.

	if (0 <= index && index < m_VisualList.size())
	{
		CVisualList::reverse_iterator p = m_VisualList.rbegin() ;
		while(p != m_VisualList.rend())
		{
			if (index-- == 0 )
			{
				return (*p)->QueryInterface(iid, (void**)ppITangramVisual) ;
			}
			p++ ;
		}
	}
	return S_FALSE ;
}

///////////////////////////////////////////////////////////
//
// ITangramWorld::CreateVisualForModel Implementation
//
HRESULT CGLWorld::CreateVisualForModel(ITangramModel* pModel)
{	
	// Preconditions.
	if (!IsValidInterface(pModel))
	{
		ASSERT(0) ;
		return E_POINTER ;
	}

	// Create the visual for this model.
	ITangramGLVisual* pVisual = NULL ;
	HRESULT hr = ::CoCreateInstance(CLSID_TangramGLVisual,
									NULL,
									CLSCTX_INPROC_SERVER,
									IID_ITangramGLVisual, 
									(void**)&pVisual) ;
	if (FAILED(hr))
	{
		ErrorMessage("Could not create visual for model.", hr) ;
		return  E_FAIL ;
	}

	// Initialize visual.
	hr = pVisual->Initialize(pModel, this) ; 
	ASSERT_HRESULT(hr) ;

	// Add visual to list.
	m_VisualList.push_back(pVisual) ;

	return S_OK ;
}

///////////////////////////////////////////////////////////
//
//	SelectVisual 
//
HRESULT CGLWorld::SelectVisual(ITangramVisual* pSelectedVisual, BOOL bSelect)
{
	// Precondition
	if (!IsValidInterface(pSelectedVisual))
	{
		ASSERT(0) ;
		return E_POINTER ;
	}

	// Move the selected object to the end of the list so that it
	// will paint last.
	pSelectedVisual->SetSelected(bSelect) ;

	if (bSelect)
	{
		// Cycle through the array looking for pSelectedVisual.
		CVisualList::iterator ppV ;
		for(ppV = m_VisualList.begin() ; ppV != m_VisualList.end() ; ppV++)
		{
			// Must compare IUnknown pointers.
			if (::InterfacesAreOnSameComponent(*ppV, pSelectedVisual))
			{
				// Move to the back of the list.
				m_VisualList.push_back(*ppV) ;
				// Remove other entry.
				m_VisualList.erase(ppV) ; 

				return S_OK ;
			}
		}
		return E_FAIL;
	}
	return S_OK ;
}

///////////////////////////////////////////////////////////
//
//	Animate
//
HRESULT __stdcall CGLWorld::Animate()
{
	// Render the OpenGL Scene.
	GLRender() ;

	// Now transfer it to the screen.	
	RECT rectDummy ;
	::SetRectEmpty(&rectDummy) ;
	HDC hdcMainWnd = ::GetDC(m_hMainWnd) ;

	HRESULT hr = m_pCanvas->Paint(hdcMainWnd, rectDummy) ;
	ASSERT_HRESULT(hr) ;

	::ReleaseDC(m_hMainWnd, hdcMainWnd);

	return S_OK ;
}


///////////////////////////////////////////////////////////
//
//					ITangramGLWorld
//
///////////////////////////////////////////////////////////
//
// ModelToDevice
//
HRESULT __stdcall CGLWorld::ModelToDevice(TangramPoint2d ptIn, TangramPoint2d* pptOut)
{
	//Precondition
	if (!IsValidAddress(pptOut, sizeof(TangramPoint2d), TRUE))
	{
		ASSERT(0) ;
		return E_POINTER ;
	}

	//Do the Isotropic scaling
	pptOut->x = ptIn.x * 2.0*(double)m_ptUpperRight.x / 20.0 ;
	pptOut->y = ptIn.y * 2.0*(double)m_ptUpperRight.y / 20.0 ;

	return S_OK ;
}

/////////////////////////////////////////////////////
//
// OpenGL Code
//

#define NUM_MATERIALS 24

#define NUM_GOOD_MATS 16  // good ones among the 24

#define NUM_TEX_MATS 4  // materials for texture

enum {
    EMERALD = 0,
    JADE,
    OBSIDIAN,
    PEARL,
    RUBY,
    TURQUOISE,
    BRASS,
    BRONZE,
    CHROME,
    COPPER,
    GOLD,
    SILVER,
    BLACK_PLASTIC,
    CYAN_PLASTIC,
    GREEN_PLASTIC,
    RED_PLASTIC,
    WHITE_PLASTIC,
    YELLOW_PLASTIC,
    BLACK_RUBBER,
    CYAN_RUBBER,
    GREEN_RUBBER,
    RED_RUBBER,
    WHITE_RUBBER,
    YELLOW_RUBBER
};

/*
int goodMaterials[NUM_GOOD_MATS] = {
        EMERALD, JADE, PEARL, RUBY, TURQUOISE, BRASS, BRONZE,
        COPPER, GOLD, SILVER, CYAN_PLASTIC, WHITE_PLASTIC, YELLOW_PLASTIC,
        CYAN_RUBBER, GREEN_RUBBER, WHITE_RUBBER };
*/
/*  materials:  emerald, jade, obsidian, pearl, ruby, turquoise
 *              brass, bronze, chrome, copper, gold, silver
 *              black, cyan, green, red, white, yellow plastic
 *              black, cyan, green, red, white, yellow rubber

    description: ambient(RGB), diffuse(RGB), specular(RGB), shininess
 *
 */
// 'tea' materials, from aux teapots program
static GLfloat teaMaterial[NUM_MATERIALS][10] = {
     0.0215f, 0.1745f, 0.0215f,
        0.07568f, 0.61424f, 0.07568f, 0.633f, 0.727811f, 0.633f, 0.6f,
     0.135f, 0.2225f, 0.1575f,
        0.54f, 0.89f, 0.63f, 0.316228f, 0.316228f, 0.316228f, 0.1f,
     0.05375f, 0.05f, 0.06625f, // XX
        0.18275f, 0.17f, 0.22525f, 0.332741f, 0.328634f, 0.346435f, 0.3f,
     0.25f, 0.20725f, 0.20725f,
        1.0f, 0.829f, 0.829f, 0.296648f, 0.296648f, 0.296648f, 0.088f,
     0.1745f, 0.01175f, 0.01175f,
        0.61424f, 0.04136f, 0.04136f, 0.727811f, 0.626959f, 0.626959f, 0.6f,
     0.1f, 0.18725f, 0.1745f,
        0.396f, 0.74151f, 0.69102f, 0.297254f, 0.30829f, 0.306678f, 0.1f,
     0.329412f, 0.223529f, 0.027451f,
        0.780392f, 0.568627f, 0.113725f, 0.992157f, 0.941176f, 0.807843f,
        0.21794872f,
     0.2125f, 0.1275f, 0.054f,
        0.714f, 0.4284f, 0.18144f, 0.393548f, 0.271906f, 0.166721f, 0.2f,
     0.25f, 0.25f, 0.25f,  // XX
        0.4f, 0.4f, 0.4f, 0.774597f, 0.774597f, 0.774597f, 0.6f,
     0.19125f, 0.0735f, 0.0225f,
        0.7038f, 0.27048f, 0.0828f, 0.256777f, 0.137622f, 0.086014f, 0.1f,
     0.24725f, 0.1995f, 0.0745f,
        0.75164f, 0.60648f, 0.22648f, 0.628281f, 0.555802f, 0.366065f, 0.4f,
     0.19225f, 0.19225f, 0.19225f,
        0.50754f, 0.50754f, 0.50754f, 0.508273f, 0.508273f, 0.508273f, 0.4f,
     0.0f, 0.0f, 0.0f, 0.01f, 0.01f, 0.01f,
        0.50f, 0.50f, 0.50f, .25f,
     0.0f, 0.1f, 0.06f, 0.0f, 0.50980392f, 0.50980392f,
        0.50196078f, 0.50196078f, 0.50196078f, .25f,
     0.0f, 0.0f, 0.0f,
        0.1f, 0.35f, 0.1f, 0.45f, 0.55f, 0.45f, .25f,
     0.0f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f, // XX
        0.7f, 0.6f, 0.6f, .25f,
     0.0f, 0.0f, 0.0f, 0.55f, 0.55f, 0.55f,
        0.70f, 0.70f, 0.70f, .25f,
     0.0f, 0.0f, 0.0f, 0.5f, 0.5f, 0.0f,
        0.60f, 0.60f, 0.50f, .25f,
     0.02f, 0.02f, 0.02f, 0.01f, 0.01f, 0.01f, // XX
        0.4f, 0.4f, 0.4f, .078125f,
     0.0f, 0.05f, 0.05f, 0.4f, 0.5f, 0.5f,
        0.04f, 0.7f, 0.7f, .078125f,
     0.0f, 0.05f, 0.0f, 0.4f, 0.5f, 0.4f,
        0.04f, 0.7f, 0.04f, .078125f,
     0.05f, 0.0f, 0.0f, 0.5f, 0.4f, 0.4f,
        0.7f, 0.04f, 0.04f, .078125f,
     0.05f, 0.05f, 0.05f, 0.5f, 0.5f, 0.5f,
        0.7f, 0.7f, 0.7f, .078125f,
     0.05f, 0.05f, 0.0f, 0.5f, 0.5f, 0.4f,
        0.7f, 0.7f, 0.04f, .078125f };

void specifyMaterial( unsigned int mIndex );

void specifyMaterial( unsigned int mIndex )
{
	if( mIndex >= NUM_MATERIALS )
	{
		mIndex = NUM_MATERIALS - 1;
	}

	GLfloat* pMat = &teaMaterial[mIndex][0] ;

	glMaterialfv (GL_FRONT, GL_AMBIENT, pMat);
	glMaterialfv (GL_BACK, GL_AMBIENT, pMat);
	pMat += 3;
	glMaterialfv (GL_FRONT, GL_DIFFUSE, pMat);
	glMaterialfv (GL_BACK, GL_DIFFUSE, pMat);
	pMat += 3;
	glMaterialfv (GL_FRONT, GL_SPECULAR, pMat);
	glMaterialfv (GL_BACK, GL_SPECULAR, pMat);
	pMat++;
	glMaterialf (GL_FRONT, GL_SHININESS, *pMat*128.0f);
	glMaterialf (GL_BACK, GL_SHININESS, *pMat*128.0f);
}

///////////////////////////////////////////////////////////
//
//	GLResize - Sizes OpenGL to fix the screen.
//
BOOL CGLWorld::GLResize(int cx, int cy) 
{

	ASSERT(m_pGL->IsCurrent()) ;

	// Set up mapping 3-D coordinates to the 2-D screen
	GLdouble gldAspect = (GLdouble) cx / (GLdouble) cy;
	m_pGL->OutputGlError("Clear") ;
	glMatrixMode(GL_PROJECTION); m_pGL->OutputGlError("MatrixMode") ;
	glLoadIdentity();
	gluPerspective(30.0, gldAspect, 1.0, 50.0);  
	glViewport(0, 0, cx, cy); 

	// Do tempory model view
	glMatrixMode(GL_MODELVIEW) ; 
	glLoadIdentity() ;
	glTranslated(0.0, 0.0, g_TranslateCameraBack) ;

	// Figure screen dimensions.
	GLdouble matrixModel[16] ; 
	GLdouble matrixProj[16];
	GLint viewport[4] ;
	glGetIntegerv(GL_VIEWPORT, viewport) ; 
	glGetDoublev( GL_MODELVIEW_MATRIX, matrixModel) ;
	glGetDoublev(GL_PROJECTION_MATRIX, matrixProj) ;

	gluUnProject((double)viewport[0], (double)viewport[1], 0.0,
					matrixModel, matrixProj, viewport,
					&m_ptLowerLeft.x, &m_ptLowerLeft.y, &m_ptLowerLeft.z) ;
	gluUnProject((double)viewport[2], (double)viewport[3], 0.0,
					matrixModel, matrixProj, viewport,
					&m_ptUpperRight.x, &m_ptUpperRight.y, &m_ptUpperRight.z) ;

	glTranslated(m_ptLowerLeft.x, m_ptLowerLeft.y, 0.0) ;
	glRotated(g_Rotation, 1.0, 0.0, 0.0) ;

	m_pGL->OutputGlError("__LINE__") ;
	return TRUE ;
}

///////////////////////////////////////////////////////////
//
// GLSetup - Prepares OpenGL for rendering.
//
static float position[] = {0.0f, 0.5f, 0.25f, 1.0f}; 

BOOL CGLWorld::GLSetup()
{
	ASSERT(m_pGL->IsCurrent()) ;

	// Enable depth calculations
	glEnable(GL_DEPTH_TEST);	m_pGL->OutputGlError("glEnable (GL_DEPTH_TEST);") ;

	// Lighting from pipe
	static float ambient[] = {1.0f, 1.0f, 1.0f, 1.0f};
	static float diffuse[] = {1.0f, 1.0f, 1.0f, 1.0f};
	static float specular[] = {1.0f, 1.0f, 1.0f, 1.0f};
	static float lmodel_ambient[] = {1.0f, 1.0f, 1.0f, 1.0f};

	glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient);
	glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
	glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);
	glLightfv(GL_LIGHT0, GL_SPECULAR, specular);
	glLightfv(GL_LIGHT0, GL_POSITION, position);

	// Enable lighting calculations
	glEnable(GL_LIGHTING) ; 		m_pGL->OutputGlError("glEnable (GL_LIGHTING) ;") ;
	glEnable(GL_LIGHT0) ;			m_pGL->OutputGlError("glEnable (GL_LIGHT0) ;") ;
	
	// Set the color to clear the screen
	glClearColor(0.0f, 0.0f, 0.0f, 0.0f) ; m_pGL->OutputGlError("__LINE__") ;

	return TRUE ;
}

///////////////////////////////////////////////////////////
//
//	GLRender - Renders the Tangram playing field.
//
BOOL CGLWorld::GLRender()
{
	ASSERT(m_pGL->IsCurrent()) ;

	// Set up some colors
	static GLdouble red[3] = {0.8, 0.0, 0.0 } ; 
	static GLdouble green[3] = {0.0, 0.75, 0.0} ;
	static GLdouble purple[3] = {1.0, 0.14, 0.6667} ; 
	static GLdouble blue[3] = {0.0, 0.0, 0.75} ;

	// Clear the color and depth buffers
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); m_pGL->OutputGlError("__LINE__") ;

	int iIndex ;
	if (m_bPicking) 
	{
		iIndex = m_VisualList.size() ;
	}
	else
	{
		// Draw table
		specifyMaterial(COPPER) ;

		glBegin(GL_POLYGON);
			glNormal3d(0.0, 0.0, 1.0) ;
			glVertex3d(0.0, 0.0, 0.0) ;
			glVertex3d(2*m_ptUpperRight.x, 0.0, 0.0) ;
			glVertex3d(2*m_ptUpperRight.x, 2*m_ptUpperRight.y, 0.0) ;
			glVertex3d(0.0, 2*m_ptUpperRight.y, 0.0) ;    
		glEnd() ;
	}

// Draw Sphere.
	specifyMaterial(GOLD) ;

	glPushMatrix() ;
	glTranslatef(position[0],position[1],position[2]) ;
	auxSolidSphere(.075) ;
	glPopMatrix() ;

	// Draw tanrgam pieces.
	specifyMaterial(SILVER) ;

	// Draw each piece.
	CVisualList::iterator p ;
	for(p = m_VisualList.begin() ; p != m_VisualList.end() ; p++)
	{
		if (m_bPicking) 
		{
			glPushName((GLuint)iIndex--) ; // Picking
		}

		(*p)->DrawOn(m_pCanvas) ;

		if (m_bPicking)
		{
			glPopName() ;
		}
	}
	glFlush() ;
	GdiFlush() ;

	m_pGL->OutputGlError("__LINE__") ;
	return TRUE ;
}

///////////////////////////////////////////////////////////
//
//	Pick - Find shape corresponding to point.
//
int CGLWorld::Pick(POINT pt)
{
	// Set Picking Mode
	m_bPicking = TRUE ;

	// Get Viewport
	GLint viewport[4] ;
	glGetIntegerv(GL_VIEWPORT, viewport) ; m_pGL->OutputGlError("__LINE__") ;

	// Set Selection Buffer.
	#define BUFSIZE 512
	GLuint selectBuf[BUFSIZE] ;
	glSelectBuffer(BUFSIZE, selectBuf) ;

	// Turn On Selection Mode.
	glRenderMode(GL_SELECT) ;

	// Initialize Name Stack
	glInitNames() ;
	// NOTE: Depends on the Visual to push the name...

	// Set Up Picking Matrix
	glMatrixMode(GL_PROJECTION) ; m_pGL->OutputGlError("__LINE__") ;
	glPushMatrix() ;
	glLoadIdentity() ;
	gluPickMatrix( (double)pt.x,
					(double)(viewport[3]-pt.y),
					5.0, 5.0,
					viewport) ;
	gluPerspective(30.0, (GLdouble)viewport[2]/(GLdouble)viewport[3], 1.0, 50.0); // Copied from OnResize!

	glMatrixMode(GL_MODELVIEW) ; 
	glPushMatrix() ;
		glLoadIdentity() ;
		glTranslated(m_ptLowerLeft.x,m_ptLowerLeft.y, g_TranslateCameraBack) ;
		glRotated(g_Rotation, 1.0, 0.0, 0.0) ;

		// Re-Render Scene for Picking.
		GLRender() ;
	glPopMatrix() ;

	glMatrixMode(GL_PROJECTION) ; 
	glPopMatrix() ;

	glFlush() ; // Just in Case.

	// Turn Off Selection Mode.
	GLint hits = glRenderMode(GL_RENDER) ; m_pGL->OutputGlError("__LINE__") ;

	// OnRender requires this...
	glMatrixMode(GL_MODELVIEW) ;

	// Find Hits
	int NameHit = 0 ;
	GLuint Z1 = 0 ;
	GLuint Zi ;

	if (hits < -1)
	{
		ASSERT(0) ;
	}
	else
	{
		GLuint* ptr = selectBuf ;
		for (int i = 0 ; i < hits ; i++)
		{
			GLuint cNames = *ptr ;
			ptr++ ;
			Zi = *ptr ;
			ptr++ ;
			ptr++ ;
			for (GLuint j = 0 ; j < cNames ; j++)
			{
				if ((Z1==0) || (Zi < Z1))
				{
					Z1 = Zi ;
					NameHit = (int)*ptr ;
				}
				ptr++ ;
			} //for j ;
		} // for i
	} //else

	// Turn Picking Mode Off
	m_bPicking = FALSE ;
	if ( (NameHit > 0) && (NameHit <= m_VisualList.size()) )
	{
		return --NameHit;
	}
	else
	{
		return -1 ;
	}
}
